package cn.anecansaitin.industrialization_expansion.client.item.ISTER;

import cn.anecansaitin.industrialization_expansion.api.common.cap.IModuleStorage;
import cn.anecansaitin.industrialization_expansion.api.common.item.IToolModule;
import cn.anecansaitin.industrialization_expansion.client.model.ExtendedModel;
import cn.anecansaitin.industrialization_expansion.common.cap.CapabilityModuleStorage;
import cn.anecansaitin.industrialization_expansion.common.item.ModifiedTool;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.vertex.IVertexBuilder;
import net.minecraft.client.renderer.Atlases;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.ItemRenderer;
import net.minecraft.client.renderer.model.BakedQuad;
import net.minecraft.client.renderer.model.IBakedModel;
import net.minecraft.client.renderer.model.ItemCameraTransforms;
import net.minecraft.client.renderer.tileentity.ItemStackTileEntityRenderer;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Direction;
import net.minecraft.util.math.vector.Vector3f;
import net.minecraftforge.client.ForgeHooksClient;
import net.minecraftforge.common.util.LazyOptional;

import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class ModifiedToolTileEntityRenderer extends ItemStackTileEntityRenderer {
    public static final ModifiedToolTileEntityRenderer INSTANCE = new ModifiedToolTileEntityRenderer();
    private static final Random random = new Random();

    @Override
    public void renderByItem(ItemStack stack, @Nonnull ItemCameraTransforms.TransformType transformType, @Nonnull MatrixStack matrix, @Nonnull IRenderTypeBuffer buffer, int combinedLight, int combinedOverlay) {
        LazyOptional<IModuleStorage> lazyModule = stack.getCapability(CapabilityModuleStorage.MODULE_STORAGE);

        if (!lazyModule.isPresent())
            return;

        IModuleStorage moduleCap = lazyModule.orElseThrow(RuntimeException::new);
        List<IBakedModel> models = new ArrayList<>();
        List<ItemStack> modules = moduleCap.getModules();
        modules.forEach((m) -> {
            if (!m.isEmpty())
                models.add(getModuleModel(m));
        });

        if (models.isEmpty())
            renderDefault(stack, transformType, matrix, buffer, combinedLight, combinedOverlay);
        else
            renderModifiedModel(models, modules, transformType, matrix, buffer, combinedLight, combinedOverlay);
    }

    private void renderDefault(ItemStack stack, ItemCameraTransforms.TransformType transformType, MatrixStack matrix, IRenderTypeBuffer buffer, int combinedLight, int combinedOverlay) {
        IBakedModel model = ExtendedModel.get(((ModifiedTool) stack.getItem()).getDefaultIcon(stack));
        matrix.pushPose();
        matrixTransform(transformType, matrix, model);
        IVertexBuilder builder = ItemRenderer.getFoilBufferDirect(buffer, Atlases.translucentCullBlockSheet(), true, false);
        renderModelLists(model, combinedLight, combinedOverlay, matrix, builder, 0xffffff);
        matrix.popPose();
    }

    private void renderModifiedModel(List<IBakedModel> models, List<ItemStack> modules, ItemCameraTransforms.TransformType transformType, MatrixStack matrix, IRenderTypeBuffer buffer, int combinedLight, int combinedOverlay) {
        matrix.pushPose();
        matrixTransform(transformType, matrix, models.get(0));
        IVertexBuilder builder = ItemRenderer.getFoilBufferDirect(buffer, Atlases.translucentCullBlockSheet(), true, false);

        for (int i = 0, modulesSize = modules.size(); i < modulesSize; i++) {
            ItemStack module = modules.get(i);

            if (module.isEmpty())
                continue;

            IToolModule item = (IToolModule) module.getItem();
            int color = item.getRenderColor();
            renderModelLists(models.get(i), combinedLight, combinedOverlay, matrix, builder, color);
        }

        matrix.popPose();
    }

    private static void matrixTransform(ItemCameraTransforms.TransformType transformType, MatrixStack matrix, IBakedModel model) {
        switch (transformType) {
            case NONE:
            case HEAD:
            case GUI:
                break;
            case THIRD_PERSON_LEFT_HAND:
                matrix.translate(0.925, -0.091, 0.605);
                ForgeHooksClient.handleCameraTransforms(matrix, model, transformType, true);
                break;
            case THIRD_PERSON_RIGHT_HAND:
                matrix.translate(0.925, -0.091, 0.605);
                ForgeHooksClient.handleCameraTransforms(matrix, model, transformType, false);
                break;
            case FIRST_PERSON_LEFT_HAND:
                matrix.translate(0.83, -0.1, 0.22);
                matrix.scale(1.15f, 1.15f, 1.15f);
                ForgeHooksClient.handleCameraTransforms(matrix, model, transformType, true);
                break;
            case FIRST_PERSON_RIGHT_HAND:
                matrix.translate(0.95, -0.1, 0.22);
                matrix.scale(1.15f, 1.15f, 1.15f);
                ForgeHooksClient.handleCameraTransforms(matrix, model, transformType, false);
                break;
            case GROUND:
                matrix.scale(0.5f, 0.5f, 0.5f);
                matrix.translate(0.5, 0.5, 0.5);
                break;
            case FIXED:
                matrix.translate(1, 0, 1);
                matrix.mulPose(Vector3f.YP.rotationDegrees(180));
                break;
        }
    }

    private void renderModelLists(IBakedModel pModel, int pCombinedLight, int pCombinedOverlay, MatrixStack pMatrixStack, IVertexBuilder pBuffer, int color) {
        for (Direction direction : Direction.values()) {
            random.setSeed(42L);
            renderQuadList(pMatrixStack, pBuffer, pModel.getQuads(null, direction, random), pCombinedLight, pCombinedOverlay, color);
        }

        random.setSeed(42L);
        renderQuadList(pMatrixStack, pBuffer, pModel.getQuads(null, null, random), pCombinedLight, pCombinedOverlay, color);
    }

    private void renderQuadList(MatrixStack pMatrixStack, IVertexBuilder pBuffer, List<BakedQuad> pQuads, int pCombinedLight, int pCombinedOverlay, int color) {
        MatrixStack.Entry matrixstack$entry = pMatrixStack.last();

        for (BakedQuad bakedquad : pQuads) {
            float f = (float) (color >> 16 & 255) / 255.0F;
            float f1 = (float) (color >> 8 & 255) / 255.0F;
            float f2 = (float) (color & 255) / 255.0F;
            pBuffer.addVertexData(matrixstack$entry, bakedquad, f, f1, f2, pCombinedLight, pCombinedOverlay, true);
        }
    }

    private IBakedModel getModuleModel(ItemStack stack) {
        return ExtendedModel.get(((IToolModule) stack.getItem()).getModelName(stack));
    }
}
